﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace PlusPlusPaint.Layers
{
    class CompositeLayer : BasicLayer
    {
        IList<ILayer> layers = new List<ILayer>();

        #region properties
        public override Point Location
        {
            get
            {
                int x = layers.Min((layer) => layer.Location.X);
                int y = layers.Min((layer) => layer.Location.Y);
                return new Point(x, y);
            }
        }

        public override Size Size
        {
            get
            {
                Point p = Location;
                int x = layers.Max((layer) => layer.Location.X + layer.Size.Width);
                int y = layers.Max((layer) => layer.Location.Y + layer.Size.Height);
                return new Size(x - p.X, y - p.Y);
            }
        }

        public override Rectangle ClientRectangle
        {
            get
            {
                return new Rectangle(Location, Size);
            }
        }

        public override bool CanFreeDraw
        {
            get
            {
                return CanFreeDraw;
            }
        }

        public override bool Explodable
        {
            get
            {
                return true;
            }
        }
        #endregion

        #region overrides
        public override IList<ILayer> ExplodeLayer()
        {
            return layers;
        }

        public override bool PositionIsInHitbox(Point p)
        {
            return layers.Count((layer) => layer.Visible && layer.PositionIsInHitbox(p)) > 0;
        }

        public override bool CanHaveToolApplied(Tools.ISketchTool t)
        {
            return t.GetType() == typeof(Tools.ObjectRemovalTool); // Only this one does anything meaningful.
        }

        // Note that very few (if any?) toolTypes are actually applicable to the sublayers.
        public override void ApplyToolToPoint(Tools.ISketchTool t, Point p)
        {
            // Special code for handling stuff that *wants* a CompositeLayer here.
            ILayer applicableLayer = layers.LastOrDefault((layer) =>
                layer.Visible && layer.PositionIsInHitbox(p) && layer.CanHaveToolApplied(t)
            );
            if (applicableLayer != null)
            {
                applicableLayer.ApplyToolToPoint(t, p);
            }
            else
            {
                throw new Exception("Internal Error:  CompositeLayer lied about tool being applicable.");
            }
        }

        public override void BurnOn(Image i)
        {
            foreach (ILayer layer in layers)
                if (layer.Visible)
                    layer.BurnOn(i);
        }

        public override ILayer Clone()
        {
            CompositeLayer copy = new CompositeLayer();
            copy.layers = (List<ILayer>)from layer in layers
                                        select layer.Clone();
            return copy;
        }

        public override SerializedObject Serialize()
        {
            List<SerializedObject> serializedLayers = (from layer in layers
                                                       select layer.Serialize()).ToList<SerializedObject>();
            // Could be moved to parent, but requires addition of property and some other
            // messy stuff.
            Dictionary<string, string> namedStrings = new Dictionary<string, string>();
            namedStrings["name"] = Name;
            return new SerializedObject("CompositeLayer", unnamedSubobjects: serializedLayers, namedStrings: namedStrings);
        }
        
        public override void Deserialize(SerializedObject s)
        {
            foreach (SerializedObject so in s.UnnamedSubobjects)
            {
                ILayer l = LayerManager.MakeLayerByName(so.Name);
                l.Deserialize(so);
                this.layers.Add(l);
            }
            Name = s.NamedStrings["name"];
        }
        #endregion

        #region public methods
        public void Add(ILayer layer)
        {
            this.layers.Add(layer);
        }
        #endregion

        #region constructors
        public CompositeLayer()
        {
            this.Visible = true;
        }
        #endregion
    }
}
